#include "pch.h"
#include "key_hook.h"
#include <stdio.h>
#include <vector>

#include <shellapi.h>
#pragma comment(lib, "Shell32.lib")

#include "hot_key.h"

static HHOOK g_handle = NULL;

static int g_status_table[0xff] = { 0 };

static std::vector<HotKey*> g_hotkey_table;

static bool IsHotKeyIdExists(int id) {
	if (g_hotkey_table.empty()) {
		return false;
	}

	for (auto hotkey : g_hotkey_table) {
		if (id == hotkey->Id()) {
			return true;
		}
	}
	return false;
}

static LRESULT CALLBACK KeyHookKeyboardProc(
	_In_ int    code,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam) {
	if (code == HC_ACTION) {

		KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*)lParam;
		if (wParam == WM_KEYDOWN) {
			g_status_table[info->vkCode] = HotKey::KEY_DOWN;

			for (auto hotkey : g_hotkey_table) {
				int prime_key = hotkey->PrimeKey();
				if (info->vkCode != prime_key) continue;

				if (hotkey->IsKeyDown()) {
					hotkey->ExecuteCallback();
				}
			}
			
		} else {
			g_status_table[info->vkCode] = HotKey::KEY_UP;
		}
	}
	return CallNextHookEx(g_handle, code, wParam, lParam);
}

KEYHOOK_API int StartKeyHook() {
	if (g_handle == NULL) {
		g_handle = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookKeyboardProc, 0, 0);
	}
	return (g_handle != NULL) ? 0 : 1;
}

KEYHOOK_API void StopKeyHook() {
	if (g_handle) {
		UnhookWindowsHookEx(g_handle);
		g_handle = NULL;
	}
}

KEYHOOK_API int RegisterGlobalHotKey(int id, int* vkcode, int vk_count,
								intptr_t user, GlobalHotKeyCallback callback) {
	if (vkcode == NULL || vk_count < 1) return 1;
	if (vk_count > HotKey::MAX_KEYS) return 2;
	if (g_handle == NULL) return 4;

	if (IsHotKeyIdExists(id)) {
		return 3;
	}

	HotKey* hotkey = new HotKey(id, user);
	hotkey->SetCallback(callback);

	for (int i = 0; i < vk_count; i++) {

		int vkc = vkcode[i];

		hotkey->AppendKey(vkc, &g_status_table[vkc]);
	}

	g_hotkey_table.push_back(hotkey);

	return 0;
}
